/*
 * Configuration.cpp
 *
 *  Created on: Sep 23, 2011
 *      Author: erudhes
 */
#include <wx/xml/xml.h>
#include <wx/dir.h>
#include <wx/filename.h>
#include "Configuration.h"

namespace Configuration
{
/**
 * Vehicle class
 */

	Vehicle::Vehicle(wxXmlNode* node): mNode(node)
	{
		wxXmlNode* child = node->GetChildren();

		mName = node->GetAttribute(wxT("name"), wxT("name"));
		while(NULL != child)
		{
			if(child->GetName() == wxT("position"))
			{
				if(wxT("x") == child->GetAttribute(wxT("name"), wxT("name")))
					mPosition.x = wxAtoi(child->GetNodeContent().c_str());
				else if(wxT("y") == child->GetAttribute(wxT("name"), wxT("name")))
					mPosition.y = wxAtoi(child->GetNodeContent().c_str());
			}
			child = child->GetNext();
		}
		mCachedPosition = mPosition;
	}

	/*virtual*/
	Vehicle::~Vehicle()
	{
		// Nothing to do yet
	}

	void Vehicle::setName(const wxString& name)
	{
		wxXmlProperty* properties = NULL;

		mName = name;
		properties = mNode->GetProperties();
		wxASSERT_MSG(properties->GetNext() == NULL, wxT("Vehicle xml element has more than one properties"));
		wxASSERT_MSG(properties->GetName() == wxT("name"), wxT("The property of the Vehicle xml element must be \"name\""));
		properties->SetValue(mName);
	}

	void Vehicle::setPosition(const wxPoint& position)
	{
		wxString nodeContent;
		std::map<wxString, wxString> properties;

		mPosition = position;
		mCachedPosition = position;
		nodeContent.Printf(wxT("%d"), mPosition.x);
		properties[wxT("name")] = wxT("x");
		Configuration::updateNodeContent(mNode, wxT("position"), nodeContent, properties);
		nodeContent.Printf(wxT("%d"), mPosition.y);
		properties[wxT("name")] = wxT("y");
		Configuration::updateNodeContent(mNode, wxT("position"), nodeContent, properties);
	}


/**
 * Level class
 */

	Level::Level(wxXmlNode* node): mVehicleA(NULL), mVehicleB(NULL), mNode(node)
	{
		wxXmlNode* child = node->GetChildren();

		mName = node->GetAttribute(wxT("name"), wxT("name"));
		while(NULL != child)
		{
			if(child->GetName() == wxT("vehicle"))
			{
				Vehicle* vehicle = new Vehicle(child);

				if(wxT("A") == vehicle->getName())
					mVehicleA = vehicle;
				else if(wxT("B") == vehicle->getName())
					mVehicleB = vehicle;
			}
			child = child->GetNext();
		}
	}

	/*virtual*/
	Level::~Level()
	{
		delete mVehicleA;
		mVehicleA = NULL;
		delete mVehicleB;
		mVehicleB = NULL;
	}

	void Level::setName(const wxString& name)
	{
		wxXmlProperty* properties = NULL;

		mName = name;
		properties = mNode->GetProperties();
		wxASSERT_MSG(properties->GetNext() == NULL, wxT("Vehicle xml element has more than one properties"));
		wxASSERT_MSG(properties->GetName() == wxT("name"), wxT("The property of the Vehicle xml element must be \"name\""));
		properties->SetValue(mName);
	}

/**
 * Levels class
 */

	Levels::Levels(wxXmlNode* node): mNode(node)
	{
		wxXmlNode* child = node->GetChildren();

		while(NULL != child)
		{
			if(child->GetName() == wxT("path"))
			{
				wxChar pathSeparator = wxFileName::GetPathSeparator();
				wxString path;
				wxString level;
				wxDir* levels = NULL;
				bool success = false;

				mPath = child->GetNodeContent();
				// On Windows we need to use backslash as the path separator
#ifdef __WXMSW__
				mPath.Replace(wxT("/"), wxT("\\"), true);
#endif
				// Open the configuration file for every level
				path = wxGetCwd() + pathSeparator + mPath + pathSeparator;
				levels = new wxDir(path);
				if(false == levels->IsOpened())
				{
					delete levels;

					return;
				}
				success = levels->GetFirst(&level, wxEmptyString, wxDIR_DIRS);
				while(true == success)
				{
					LevelData levelData;
					wxXmlDocument* levelConfig = new wxXmlDocument();
					wxXmlNode* child = NULL;

					do
					{
						if(false == levelConfig->Load(path + level + pathSeparator + wxT("config.xml")))
						{
							delete levelConfig;
							break;
						}
						if(levelConfig->GetRoot()->GetName() != wxT("pixel-by-pixel"))
						{
							delete levelConfig;
							break;
						}
						levelData.mConfigFile = levelConfig;
						child = levelConfig->GetRoot()->GetChildren();
						while(NULL != child)
						{
							if(child->GetName() == wxT("level"))
							{
								levelData.mLevel = new Level(child);
								mLevels[level] = levelData;
							}
							child = child->GetNext();
						}
					}
					while(false);
					success = levels->GetNext(&level);
				}
				delete levels;
			}
			child = child->GetNext();
		}
	}

	/*virtual*/
	Levels::~Levels()
	{
		wxChar pathSeparator = wxFileName::GetPathSeparator();
		wxString path = wxGetCwd() + pathSeparator + mPath + pathSeparator;

		for(std::map<wxString, LevelData>::iterator levelsIterator = mLevels.begin(); levelsIterator != mLevels.end(); levelsIterator++)
		{
			// Save the xml files for the levels
			levelsIterator->second.mConfigFile->Save(path + levelsIterator->second.mLevel->getName() + pathSeparator + wxT("config.xml"));
			delete levelsIterator->second.mLevel;
			delete levelsIterator->second.mConfigFile;
			levelsIterator->second.mLevel = NULL;
			levelsIterator->second.mConfigFile = NULL;
		}
		mLevels.clear();
	}

	void Levels::setPath(const wxString& path)
	{
		wxString nodeContent;

		mPath = path;
		nodeContent = mPath;
		// On Windows we need to change back the path separator to slashes
#ifdef __WXMSW__
		nodeContent.Replace(wxT("\\"), wxT("/"), true);
#endif
		Configuration::updateNodeContent(mNode, wxT("path"), nodeContent);
	}

	Level* Levels::createNewLevel(const wxString& name)
	{
		wxChar pathSeparator = wxFileName::GetPathSeparator();
		wxString levelPath = wxGetCwd() + pathSeparator + mPath + pathSeparator + name + pathSeparator;
		wxDir levelDir(levelPath);
		Level* returnLevel = NULL;

		if(true == levelDir.IsOpened() || true == wxMkdir(levelPath))
		{
			// Build up the needed xml structure
			wxXmlDocument* configuration = new wxXmlDocument();
			wxXmlNode* rootNode = new wxXmlNode(NULL, wxXML_ELEMENT_NODE, wxT("pixel-by-pixel"));
			wxXmlNode* newLevel = new wxXmlNode(rootNode, wxXML_ELEMENT_NODE, wxT("level"));
			wxXmlNode* vehicleA = new wxXmlNode(newLevel, wxXML_ELEMENT_NODE, wxT("vehicle"));
			wxXmlNode* vehicleB = new wxXmlNode(newLevel, wxXML_ELEMENT_NODE, wxT("vehicle"));
			wxXmlNode* positionAx = new wxXmlNode(vehicleA, wxXML_ELEMENT_NODE, wxT("position"));
			wxXmlNode* positionAy = new wxXmlNode(vehicleA, wxXML_ELEMENT_NODE, wxT("position"));
			wxXmlNode* positionBx = new wxXmlNode(vehicleB, wxXML_ELEMENT_NODE, wxT("position"));
			wxXmlNode* positionBy = new wxXmlNode(vehicleB, wxXML_ELEMENT_NODE, wxT("position"));
			LevelData levelData;

			configuration->SetRoot(rootNode);
			newLevel->AddProperty(wxT("name"), name);
			vehicleA->AddProperty(wxT("name"), wxT("A"));
			vehicleB->AddProperty(wxT("name"), wxT("B"));
			positionAx->AddProperty(wxT("name"), wxT("x"));
			positionAy->AddProperty(wxT("name"), wxT("y"));
			positionBx->AddProperty(wxT("name"), wxT("x"));
			positionBy->AddProperty(wxT("name"), wxT("y"));
			positionAx->AddChild(new wxXmlNode(positionAx, wxXML_TEXT_NODE, wxT("")));
			positionAy->AddChild(new wxXmlNode(positionAy, wxXML_TEXT_NODE, wxT("")));
			positionBx->AddChild(new wxXmlNode(positionBx, wxXML_TEXT_NODE, wxT("")));
			positionBy->AddChild(new wxXmlNode(positionBy, wxXML_TEXT_NODE, wxT("")));
			levelData.mLevel = new Level(newLevel);
			levelData.mConfigFile = configuration;
			mLevels[name] = levelData;

			returnLevel = levelData.mLevel;
		}

		return returnLevel;
	}

	Level* Levels::addNewLevel(const wxString& name)
	{
		wxChar pathSeparator = wxFileName::GetPathSeparator();
		wxString configPath = wxGetCwd() + pathSeparator + mPath + pathSeparator + name + pathSeparator + wxT("config.xml");
		LevelData levelData;
		wxXmlDocument* levelConfig = new wxXmlDocument();
		wxXmlNode* child = NULL;

		do
		{
			if(false == levelConfig->Load(configPath))
			{
				delete levelConfig;
				break;
			}
			if(levelConfig->GetRoot()->GetName() != wxT("pixel-by-pixel"))
			{
				delete levelConfig;
				break;
			}
			levelData.mConfigFile = levelConfig;
			child = levelConfig->GetRoot()->GetChildren();
			while(NULL != child)
			{
				if(child->GetName() == wxT("level"))
				{
					levelData.mLevel = new Level(child);
					mLevels[name] = levelData;
				}
				child = child->GetNext();
			}
		}
		while(false);

		return levelData.mLevel;
	}

/**
 * ProxyAddress class
 */

	ProxyAddress::ProxyAddress(wxXmlNode* node): mHost(), mPort(-1), mNode(node)
	{
		wxXmlNode* child = node->GetChildren();

		while(NULL != child)
		{
			if(child->GetName() == wxT("host"))
				mHost = child->GetNodeContent();
			if(child->GetName() == wxT("port"))
				mPort = wxAtoi(child->GetNodeContent().c_str());
			child = child->GetNext();
		}
	}

	/*virtual*/
	ProxyAddress::~ProxyAddress()
	{
		// Nothing to do yet
	}

	void ProxyAddress::setHost(const wxString& host)
	{
		wxString nodeContent;

		mHost = host;
		nodeContent = mHost;
		Configuration::updateNodeContent(mNode, wxT("host"), nodeContent);
	}

	void ProxyAddress::setPort(int port)
	{
		wxString nodeContent;

		mPort = port;
		nodeContent.Printf(wxT("%d"), mPort);
		Configuration::updateNodeContent(mNode, wxT("port"), nodeContent);
	}

/**
 * ProxyConfiguration class
 */

	ProxyConfiguration::ProxyConfiguration(wxXmlNode* node): mUseProxy(false), mProxyAddress(NULL), mNode(node)
	{
		wxXmlNode* child = node->GetChildren();

		while(NULL != child)
		{
			if(child->GetName() == wxT("use-proxy"))
			{
				if(child->GetNodeContent() == wxT("yes"))
					mUseProxy = true;
				else
					mUseProxy = false;
			}
			if(child->GetName() == wxT("proxy-address"))
			{
				wxASSERT_MSG(mProxyAddress == NULL, wxT("More than one proxy address added to the config.xml"));
				mProxyAddress = new ProxyAddress(child);
			}
			child = child->GetNext();
		}
	}

	/*virtual*/
	ProxyConfiguration::~ProxyConfiguration()
	{
		// Nothing to do yet
	}

	void ProxyConfiguration::setUseProxy(bool useProxy)
	{
		wxString nodeContent;

		mUseProxy = useProxy;
		if(true == mUseProxy)
			nodeContent = wxT("yes");
		else
			nodeContent = wxT("no");
		Configuration::updateNodeContent(mNode, wxT("use-proxy"), nodeContent);
	}

/**
 * NetworkConfiguration class
 */

	NetworkConfiguration::NetworkConfiguration(wxXmlNode* node): mPortNumber(-1),
																 mFrequency(-1),
																 mCachedFrequency(-1),
																 mFetchOnlyFreeServers(true),
																 mDownloadUnknownLevels(true),
																 mUploadLevels(false),
																 mRegisterServer(true),
																 mProxyConfiguration(NULL),
																 mNode(node)
	{
		wxXmlNode* child = node->GetChildren();

		while(NULL != child)
		{
			if(child->GetName() == wxT("port-number"))
				mPortNumber = wxAtoi(child->GetNodeContent().c_str());
			if(child->GetName() == wxT("data-sending-frequency"))
				mFrequency = wxAtoi(child->GetNodeContent().c_str());
			if(child->GetName() == wxT("only-free-servers"))
			{
				if(wxT("yes") == child->GetNodeContent())
					mFetchOnlyFreeServers = true;
				else
					mFetchOnlyFreeServers = false;
			}
			if(child->GetName() == wxT("download-unknown-levels"))
			{
				if(wxT("yes") == child->GetNodeContent())
					mDownloadUnknownLevels = true;
				else
					mDownloadUnknownLevels = false;
			}
			if(child->GetName() == wxT("upload-levels"))
			{
				if(wxT("yes") == child->GetNodeContent())
					mUploadLevels = true;
				else
					mUploadLevels = false;
			}
			if(child->GetName() == wxT("register-server"))
			{
				if(wxT("yes") == child->GetNodeContent())
					mRegisterServer = true;
				else
					mRegisterServer = false;
			}
			if(child->GetName() == wxT("proxy-settings"))
				mProxyConfiguration = new ProxyConfiguration(child);
			child = child->GetNext();
		}
		mCachedFrequency = mFrequency;
	}

	/*virtual*/
	NetworkConfiguration::~NetworkConfiguration()
	{
		// Nothing to do yet
	}

	void NetworkConfiguration::setPortNumber(int portNumber)
	{
		wxString nodeContent;

		mPortNumber = portNumber;
		nodeContent.Printf(wxT("%d"), mPortNumber);
		Configuration::updateNodeContent(mNode, wxT("port-number"), nodeContent);
	}

	void NetworkConfiguration::setFrequency(int frequency)
	{
		wxString nodeContent;

		mFrequency = frequency;
		mCachedFrequency = frequency;
		nodeContent.Printf(wxT("%d"), mFrequency);
		Configuration::updateNodeContent(mNode, wxT("data-sending-frequency"), nodeContent);
	}

	void NetworkConfiguration::fetchOnlyFreeServers(bool fetchOnlyFreeServers)
	{
		wxString nodeContent;

		mFetchOnlyFreeServers = fetchOnlyFreeServers;
		if(true == fetchOnlyFreeServers)
			nodeContent = wxT("yes");
		else
			nodeContent = wxT("no");
		Configuration::updateNodeContent(mNode, wxT("only-free-servers"), nodeContent);
	}

	void NetworkConfiguration::downloadUnknownLevels(bool downloadUnknownLevels)
	{
		wxString nodeContent;

		mDownloadUnknownLevels = downloadUnknownLevels;
		if(true == downloadUnknownLevels)
			nodeContent = wxT("yes");
		else
			nodeContent = wxT("no");
		Configuration::updateNodeContent(mNode, wxT("download-unknown-levels"), nodeContent);
	}

	void NetworkConfiguration::uploadLevels(bool uploadLevels)
	{
		wxString nodeContent;

		mUploadLevels = uploadLevels;
		if(true == uploadLevels)
			nodeContent = wxT("yes");
		else
			nodeContent = wxT("no");
		Configuration::updateNodeContent(mNode, wxT("upload-levels"), nodeContent);
	}

	void NetworkConfiguration::registerServer(bool registerServer)
	{
		wxString nodeContent;

		mRegisterServer = registerServer;
		if(true == registerServer)
			nodeContent = wxT("yes");
		else
			nodeContent = wxT("no");
		Configuration::updateNodeContent(mNode, wxT("register-server"), nodeContent);
	}

/**
 * Configuration class
 */

	/*static*/
	bool Configuration::updateNodeContent(wxXmlNode* parent, const wxString& nodeName, const wxString& nodeContent, const std::map<wxString, wxString>& properties)
	{
		wxXmlNode* child = parent->GetChildren();
		bool returnValue = false;

		while(NULL != child)
		{
			if(child->GetName() == nodeName)
			{
				bool isPropertiesOK = false;
				wxXmlProperty* xmlProperties = child->GetProperties();

				if(0 == properties.size())
					isPropertiesOK = true;
				// Check the properties
				while(NULL != xmlProperties && false == isPropertiesOK)
				{
					if(properties.at(xmlProperties->GetName()) == xmlProperties->GetValue())
						isPropertiesOK = true;
					xmlProperties = xmlProperties->GetNext();
				}
				if(true == isPropertiesOK)
				{
					child->GetChildren()->SetContent(nodeContent);
					returnValue = true;
					break;
				}
			}
			child = child->GetNext();
		}

		return returnValue;
	}

	Configuration::Configuration(): mLogoPath(wxT("")), mDotSize(-1), mCachedDotSize(-1), mNetworkConfiguration(NULL), mLevels(NULL), mXmlConfig(NULL)
	{
		wxXmlNode* child = NULL;

		mXmlConfig = new wxXmlDocument();
		// Load the configuration file
		if(false == mXmlConfig->Load(wxT("config.xml")))
		{
			delete mXmlConfig;
			mXmlConfig = NULL;
			return;
		}
		if(mXmlConfig->GetRoot()->GetName() != wxT("pixel-by-pixel"))
		{
			delete mXmlConfig;
			mXmlConfig = NULL;
			return;
		}
		child = mXmlConfig->GetRoot()->GetChildren();
		while(NULL != child)
		{
			if(child->GetName() == wxT("network"))
				mNetworkConfiguration = new NetworkConfiguration(child);
			if(child->GetName() == wxT("levels"))
				mLevels = new Levels(child);
			if(child->GetName() == wxT("logo"))
			{
				mLogoPath = child->GetNodeContent();
				// On Windows we need to use backslash as the path separator
#ifdef __WXMSW__
				mLogoPath.Replace(wxT("/"), wxT("\\"), true);
#endif
			}
			if(child->GetName() == wxT("dot-size"))
				mDotSize = wxAtoi(child->GetNodeContent().c_str());
			child = child->GetNext();
		}
		mCachedDotSize = mDotSize;
	}

	/*virtual*/
	Configuration::~Configuration()
	{
		delete mNetworkConfiguration;
		mNetworkConfiguration = NULL;
		delete mLevels;
		mLevels = NULL;
		mXmlConfig->Save(wxT("config.xml"));
		delete mXmlConfig;
		mXmlConfig = NULL;
	}

	void Configuration::setLogoPath(const wxString& logoPath)
	{
		wxString nodeContent;

		mLogoPath = logoPath;
		nodeContent = mLogoPath;
#ifdef __WXMSW__
		nodeContent.Replace(wxT("\\"), wxT("/"), true);
#endif
		Configuration::updateNodeContent(mXmlConfig->GetRoot(), wxT("logo"), nodeContent);
	}

	void Configuration::setDotSize(int dotSize)
	{
		wxString nodeContent;

		mDotSize = dotSize;
		mCachedDotSize = dotSize;
		nodeContent.Printf(wxT("%d"), mDotSize);
		Configuration::updateNodeContent(mXmlConfig->GetRoot(), wxT("dot-size"), nodeContent);
	}

/**
 * CommonConfigurationPacket class
 */

	CommonConfigurationPacket::CommonConfigurationPacket(Configuration* configuration, const wxString& level)
	{
		wxPoint positionA = configuration->getLevels()->getLevel(level)->getVehicleA()->getPosition();
		wxPoint positionB = configuration->getLevels()->getLevel(level)->getVehicleB()->getPosition();

		mDotSize = htonl(configuration->getDotSize());
		mFrequency = htonl(configuration->getNetworkConfiguration()->getFrequency());
		mCoordinateAx = htonl(positionA.x);
		mCoordinateAy = htonl(positionA.y);
		mCoordinateBx = htonl(positionB.x);
		mCoordinateBy = htonl(positionB.y);
	}

	CommonConfigurationPacket::CommonConfigurationPacket(const char* buffer)
	{
		mDotSize = *((int*)(buffer));
		buffer += sizeof(int);
		mFrequency = *((int*)(buffer));
		buffer += sizeof(int);
		mCoordinateAx = *((int*)(buffer));
		buffer += sizeof(int);
		mCoordinateAy = *((int*)(buffer));
		buffer += sizeof(int);
		mCoordinateBx = *((int*)(buffer));
		buffer += sizeof(int);
		mCoordinateBy = *((int*)(buffer));
	}

	CommonConfigurationPacket::~CommonConfigurationPacket()
	{
		// Nothing to do yet
	}
}
